<script setup>
import {
  ref,
  watch,
  provide,
  nextTick,
  reactive,
  onMounted,
  onUnmounted,
} from "vue";
import { isArray, isFunction, merge, isObject, isUndefined } from "lodash";
import defaultOptions from "@cwd/wd-crud/js/defaultOptions";
import { loadDict } from "@cwd/wd-form/js/networkRequest.js";
import config from "@cwd/config/crud";
import WdColumn from "./components/wd-column";
import WdSearch from "./components/wd-search.vue";
import WdSetting from "./components/wd-setting.vue";
import { message } from "@/utils/message";
import WdCrudForm from "./components/wd-crud-form";
import request from "@/axios";
import tool from "@/utils/tool";

const props = defineProps({
  // 表格数据
  data: { type: [Function, Array], default: () => null },
  // 增删改查设置
  options: { type: Object, default: {} },
  crud: { type: Object, default: {} },
  // 字段列设置
  columns: { type: Array, default: [] },
});

const loading = ref(false); //表格加载状态
const dicts = ref({}); // 关联字典
const cascaders = ref([]); // 多级选择

const requestParams = ref({});
const currentApi = ref();
const wdTableRef = ref(null);
const isRecovery = ref(false);
const openPagination = ref(true);
const crudFooterRef = ref(null);
const total = ref(0);
const tableIsShow = ref(true);
const slots = ref([]);
// 表格布局
const WdSearchRef = ref(null);
const crudContentRef = ref(null);
const crudColumnRef = ref(null);
const crudHeaderRef = ref();

// 表格表单
const crudFormRef = ref(null);
//表格设置
const crudSettingRef = ref(null);
//表格数据
const tableData = ref([]);
const reloadColumn = ref(true);
const multipleSelection = ref([]);
const headerHeight = ref(0);
//当前表格配置
const tableOptions = ref(
  merge(
    {},
    JSON.parse(JSON.stringify(defaultOptions)),
    props.options,
    props.crud
  )
);
//是否开启批量操作
const isBatch = (obj) => (isUndefined(obj) ? true : obj?.batch ?? true);

//当前组件列
const columns = ref(props.columns);

const getTableData = () => {
  return tableData.value;
};
// 初始化
const init = async () => {
  // 设置 组件id
  if (isUndefined(tableOptions.value.id)) {
    tableOptions.value.id =
      "WdCrud_" +
      Math.floor(
        Math.random() * 100000 + Math.random() * 20000 + Math.random() * 5000
      );
  }
  // 收集数据
  columns.value.map((item) => {
    if (item.cascaderItem && item.cascaderItem.length > 0) {
      cascaders.value.push(...item.cascaderItem);
    }
    columns.value.width = columns.value.width ?? defaultOptions.columnWidth;
  });
  await columns.value.map(async (item) => {
    // 字典
    if (!cascaders.value.includes(item.dataIndex) && item.dict) {
      await loadDict(dicts.value, item);
    }
  });
};
//拼合请求参数
const requestData = async () => {
  await init();
  //追加操作列
  if (
    tableOptions.value.operationColumn &&
    columns.value.length > 0 &&
    columns.value[columns.value.length - 1].dataIndex !== "__operation"
  ) {
    columns.value.push({
      title: tableOptions.value.operationColumnText,
      dataIndex: "__operation",
      width:
        tableOptions.value.operationColumnWidth ??
        tableOptions.value.operationWidth,
      align: tableOptions.value.operationColumnAlign,
      fixed: tableOptions.value.operationColumnFixed,
    });
  }
  //初始化请求参数
  initRequestParams();
  await refresh();
};

//初始化请求参数
const initRequestParams = () => {
  requestParams.value[config.request.page] = 1;
  requestParams.value[config.request.pageSize] =
    tableOptions.value.pageSize ?? 10;
  if (tableOptions.value.requestParamsLabel) {
    requestParams.value[tableOptions.value.requestParamsLabel] =
      tableOptions.value.requestParams;
  } else {
    requestParams.value = Object.assign(
      requestParams.value,
      tableOptions.value.requestParams
    );
  }
};
//筛选请求
const searchSubmitHandler = async (formData) => {
  if (
    tableOptions.value.requestParamsLabel &&
    requestParams.value[tableOptions.value.requestParamsLabel]
  ) {
    requestParams.value[tableOptions.value.requestParamsLabel] = Object.assign(
      requestParams.value[options.value.requestParamsLabel],
      formData
    );
  } else if (tableOptions.value.requestParamsLabel) {
    requestParams.value[tableOptions.value.requestParamsLabel] = Object.assign(
      {},
      formData
    );
  } else {
    requestParams.value = Object.assign(requestParams.value, formData);
  }
  if (
    tableOptions.value.beforeSearch &&
    isFunction(tableOptions.value.beforeSearch)
  ) {
    options.value.beforeSearch(requestParams.value);
  }
  await pageChangeHandler(1);
  if (
    tableOptions.value.afterSearch &&
    isFunction(tableOptions.value.afterSearch)
  ) {
    tableOptions.value.afterSearch(requestParams.value);
  }
};

//刷新
const refresh = async () => {
  currentApi.value =
    isRecovery.value &&
    tableOptions.value.recycleApi &&
    isFunction(tableOptions.value.recycleApi)
      ? tableOptions.value.recycleApi
      : tableOptions.value.api;
  await requestHandle();
};
//发起请求
const requestHandle = async () => {
  loading.value = true;
  isFunction(tableOptions.value.beforeRequest) &&
    tableOptions.value.beforeRequest(requestParams.value);
  if (isFunction(currentApi.value)) {
    const response = config.parseResponseData(
      await currentApi.value(requestParams.value)
    );
    if (response.rows) {
      tableData.value = response.rows;
      if (response.pageInfo) {
        total.value = response.pageInfo.total;
        openPagination.value = true;
      } else {
        openPagination.value = false;
      }
    } else {
      tableData.value = response;
    }
  } else {
    console.log(currentApi.value);
    console.error(`wd-crud error：crud.api not is Function.`);
  }
  isFunction(tableOptions.value.afterRequest) &&
    (tableData.value = tableOptions.value.afterRequest(tableData.value));
  loading.value = false;
};
//挂在请求数据
onMounted(async () => {
  if (
    typeof tableOptions.value.autoRequest == "undefined" ||
    tableOptions.value.autoRequest
  ) {
    await requestData();
  }
});
//分页数量
const pageSizeChangeHandler = async (pageSize) => {
  requestParams.value[config.request.page] = 1;
  requestParams.value[config.request.pageSize] = pageSize;
  await refresh();
};
//切换分页
const pageChangeHandler = async (currentPage) => {
  requestParams.value[config.request.page] = currentPage;
  await refresh();
};
//切换筛选表单
const toggleSearch = async () => {
  const dom = crudHeaderRef.value?.style;
  if (dom) {
    WdSearchRef.value.showSearch
      ? WdSearchRef.value.setSearchHidden()
      : WdSearchRef.value.setSearchDisplay();

    await nextTick(() => {
      headerHeight.value = crudHeaderRef.value.offsetHeight;
      tableOptions.value.pageLayout === "fixed" && settingFixedPage();
    });
  }
};

const settingFixedPage = () => {
  const workAreaHeight = document.querySelector(".work-area").offsetHeight;
  const tableHeight =
    workAreaHeight -
    headerHeight.value -
    (openPagination.value ? 160 : 116) +
    (total.value === 0 && !loading.value ? 44 : 0);
  crudContentRef.value.style.height = tableHeight + "px";
};

//切换数据(回收站)
const switchDataType = async () => {
  isRecovery.value = !isRecovery.value;
  currentApi.value =
    isRecovery.value &&
    tableOptions.value.recycleApi &&
    isFunction(tableOptions.value.recycleApi)
      ? tableOptions.value.recycleApi
      : tableOptions.value.api;
  await requestData();
};

//获取动态插槽
const getSlot = (cls = []) => {
  let sls = [];
  cls.map((item) => {
    if (item.children && item.children.length > 0) {
      let tmp = getSlot(item.children);
      sls.push(...tmp);
    } else if (item.dataIndex) {
      sls.push(item.dataIndex);
    }
  });
  return sls;
};

slots.value = getSlot(props.columns);
//编辑操作
const editAction = (record) => {
  if (
    isFunction(tableOptions.value.beforeOpenEdit) &&
    !tableOptions.value.beforeOpenEdit(record)
  ) {
    return false;
  }
  if (
    tableOptions.value.edit.action &&
    isFunction(tableOptions.value.edit.action)
  ) {
    tableOptions.value.edit.action(record);
  } else {
    crudFormRef.value.edit(record);
  }
};
//添加操作
const addAction = () => {
  if (
    isFunction(tableOptions.value.beforeOpenAdd) &&
    !tableOptions.value.beforeOpenAdd()
  ) {
    return false;
  }
  if (
    tableOptions.value.add.action &&
    isFunction(tableOptions.value.add.action)
  ) {
    tableOptions.value.add.action();
  } else {
    crudFormRef.value.add();
  }
};
// 批量删除操作
const deletesMultipleAction = async () => {
  if (multipleSelection.value && multipleSelection.value.length > 0) {
    const api = isRecovery.value
      ? tableOptions.value.delete.realApi
      : tableOptions.value.delete.api;
    let data = {};
    if (
      isFunction(tableOptions.value.beforeDelete) &&
      !(data = tableOptions.value.beforeDelete(multipleSelection.value))
    ) {
      return false;
    }
    const response = await api(
      Object.assign({ ids: multipleSelection.value }, data)
    );
    if (
      tableOptions.value.afterDelete &&
      isFunction(tableOptions.value.afterDelete)
    ) {
      tableOptions.value.afterDelete(response);
    }
    response.success &&
      message(response.message || `删除成功！`, { type: "success" });
    multipleSelection.value = [];
    await refresh();
  } else {
    message("至少选择一条数据", { type: "error" });
  }
};
//批量恢复
const recoverysMultipleAction = async () => {
  if (multipleSelection.value && multipleSelection.value.length > 0) {
    const response = await tableOptions.value.recovery.api({
      ids: multipleSelection.value,
    });
    response.success &&
      message(response.message || `恢复成功！`, { type: "success" });
    multipleSelection.value = [];
    await refresh();
  } else {
    message("至少选择一条数据", { type: "error" });
  }
};

//表格多选监听
const handleSelectionChange = (val) => {
  const ids = val.map((item) => item[tableOptions.value.pk]);
  multipleSelection.value = ids;
};
//初始化搜索列
const initSearchColumns = () => WdSearchRef.value.initSearchColumns();
//更新列
const changeColumn = async () => {
  tableIsShow.value = false;
  await nextTick(() => (tableIsShow.value = true));
};
//表格设置
const tableSetting = () => {
  crudSettingRef.value.open();
};
//表单提交成功后
const requestSuccess = async (response) => {
  if (response && response.code && response.code === 200) {
    tableOptions.value.dataCompleteRefresh && (await refresh());
    if (reloadColumn.value) {
      reloadColumn.value = false;
      await nextTick(() => {
        reloadColumn.value = true;
      });
    }
  }
};

const dictTrans = (dataIndex, value) => {
  if (dicts.value[dataIndex] && dicts.value[dataIndex].tran) {
    return dicts.value[dataIndex].tran[value];
  } else {
    return value;
  }
};

const dictColors = (dataIndex, value) => {
  if (dicts.value[dataIndex] && dicts.value[dataIndex].colors) {
    return dicts.value[dataIndex].colors[value];
  } else {
    return undefined;
  }
};

const getSearchSlot = () => {
  let sls = [];
  columns.value.map((item) => {
    if (item.search && item.search === true) {
      sls.push(item.dataIndex);
    }
  });
  return sls;
};
// 导入
// const importAction = () =>

//导入操作
const importAction = () => {
  if (
    isFunction(tableOptions.value.beforeOpenImport) &&
    !tableOptions.value.beforeOpenImport()
  ) {
    return false;
  }
  if (
    tableOptions.value.beforeOpenImport.action &&
    isFunction(tableOptions.value.beforeOpenImport.action)
  ) {
    tableOptions.value.beforeOpenImport.action(record);
  } else {
    crudImportRef.value.open();
  }
};

const exportAction = () => {
  const data = tableOptions.value.requestParamsLabel
    ? requestParams.value[tableOptions.value.requestParamsLabel]
    : requestParams.value;

  const download = (url) =>
    request.request({
      url: url,
      method: "post",
      data: data,
      responseType: "blob",
      header: { timeout: 60 * 1000 },
    });

  download(tableOptions.value.export.url)
    .then((res) => {
      tool.download(res);
      message("请求成功，文件开始下载", { type: "success" });
    })
    .catch(() => {
      message("请求服务器错误，下载失败", { type: "error" });
    });
};

provide("options", tableOptions.value);
provide("columns", columns);
provide("requestParams", requestParams.value);
provide("dicts", dicts);
provide("dictColors", dictColors);
provide("dictTrans", dictTrans);
provide("isRecovery", isRecovery);
defineExpose({
  refresh,
  requestData,
  addAction,
  editAction,
  getTableData,
});
</script>
<template>
  <div class="__crud-container">
    <div class="_wd-crud-content">
      <div
        class="_crud-header flex py-4 flex-col flex-wrap"
        ref="crudHeaderRef">
        <wd-search ref="WdSearchRef" @search="searchSubmitHandler">
          <template
            v-for="(slot, slotIndex) in getSearchSlot()"
            :key="slotIndex"
            #[slot]="{ searchForm, component }">
            <slot :name="`search-${slot}`" v-bind="{ searchForm, component }" />
          </template>
        </wd-search>
      </div>
      <div class="operation-tools flex w-full py-2">
        <el-row class="wd-operation-tools flex flex-1" justify="space-around">
          <el-col
            class="tw-flex tw-justify-start"
            :xs="24"
            :sm="24"
            :md="12"
            :lg="12"
            :xl="12">
            <el-button
              type="primary"
              v-if="tableOptions.add.show"
              @click="addAction">
              <template #icon>
                <icon icon="vi-ant-design:plus-outlined"></icon>
              </template>
              新建
            </el-button>
            <el-popconfirm
              width="190"
              v-if="tableOptions.delete.show"
              title="确定要批量删除数据吗?"
              @confirm="deletesMultipleAction()">
              <template #reference>
                <el-button type="danger">
                  <template #icon>
                    <icon icon="vi-ant-design:delete-outlined"></icon>
                  </template>
                  删除
                </el-button>
              </template>
            </el-popconfirm>
            <el-popconfirm
              width="190"
              title="确定要批量恢复数据吗?"
              @confirm="recoverysMultipleAction()"
              v-if="
                tableOptions.recovery.show &&
                isRecovery &&
                isBatch(tableOptions.delete)
              ">
              <template #reference>
                <el-button type="success">
                  <template #icon>
                    <icon icon="vi-tdesign:refresh"></icon>
                  </template>
                  恢复
                </el-button>
              </template>
            </el-popconfirm>
            <el-button
              type="primary"
              v-if="tableOptions.import.show"
              @click="importAction">
              <template #icon>
                <icon icon="ri-upload-line"></icon>
              </template>
              {{ tableOptions.import.text || "导入" }}
            </el-button>
            <el-button
              type="primary"
              v-if="tableOptions.export.show"
              @click="exportAction">
              <template #icon>
                <icon icon="ri-download-line"></icon>
              </template>
              {{ tableOptions.export.text || "导出" }}
            </el-button>
            <slot name="tableAfterButtons"></slot>
          </el-col>
          <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
            <div class="w-full flex justify-end">
              <el-tooltip
                effect="dark"
                :content="isRecovery ? '显示正常数据' : '显示回收站数据'"
                placement="top"
                v-if="
                  tableOptions.recycleApi && isFunction(tableOptions.recycleApi)
                ">
                <el-button
                  circle
                  :type="isRecovery ? 'danger' : 'default'"
                  @click="switchDataType">
                  <template #icon>
                    <icon icon="vi-tdesign:arrow-left-right-3"></icon>
                  </template>
                </el-button>
              </el-tooltip>
              <el-tooltip effect="dark" content="刷新表格数据" placement="top">
                <el-button circle @click="refresh">
                  <template #icon>
                    <icon icon="vi-tdesign:refresh"></icon>
                  </template>
                </el-button>
              </el-tooltip>
              <el-tooltip effect="dark" content="显隐搜索" placement="top">
                <el-button circle @click="toggleSearch">
                  <template #icon>
                    <icon icon="vi-tdesign:search"></icon>
                  </template>
                </el-button>
              </el-tooltip>
              <el-tooltip
                effect="dark"
                content="设置"
                v-if="1 == 2"
                placement="top">
                <el-button circle @click="tableSetting">
                  <template #icon>
                    <icon icon="ri-settings-5-line"></icon>
                  </template>
                </el-button>
              </el-tooltip>
            </div>
          </el-col>
        </el-row>
      </div>
    </div>
    <div
      ref="crudContentRef"
      :class="[tableOptions.outBordered ? '__crud_bordered' : '']"
      class="tw-mt-4">
      <slot name="crudContent" v-bind="tableData">
        <el-table
          ref="wdTableRef"
          min-height="60vh"
          max-height="60vh"
          :data="tableData"
          :size="tableOptions.size"
          :border="tableOptions.bordered"
          :stripe="tableOptions.stripe"
          :table-layout="tableOptions.pageLayout"
          :row-key="
            (record) => {
              return record[tableOptions.pk];
            }
          "
          :indent="32"
          v-loading="loading"
          @selection-change="handleSelectionChange"
          class="w-full"
          v-if="
            (!tableOptions.expandAllRows || tableData.length > 0) && tableIsShow
          ">
          <!-- 索引列 -->
          <el-table-column
            type="index"
            label="序号"
            align="center"
            :width="tableOptions.indexColumnWidth ?? 60"
            v-if="tableOptions.showIndex ?? false">
          </el-table-column>
          <!-- 追加选中列 -->
          <el-table-column
            type="selection"
            align="center"
            v-if="tableOptions.rowSelection"
            :fixed="true"
            :width="tableOptions.rowSelection?.width ?? 60"></el-table-column>
          <wd-column
            ref="crudColumnRef"
            v-if="reloadColumn"
            :isRecovery="isRecovery"
            :columns="columns"
            :crudFormRef="crudFormRef"
            @refresh="() => refresh()">
            <template #operationBeforeExtend="{ record }">
              <slot name="operationBeforeExtend" v-bind="{ record }"></slot>
            </template>

            <template #operationCell="{ record }">
              <slot name="operationCell" v-bind="{ record }"></slot>
            </template>
            <template
              v-for="(slot, slotIndex) in slots"
              :key="slotIndex"
              #[slot]="{ record, scope }">
              <slot :name="`${slot}`" v-bind="{ record, scope }"></slot>
            </template>
          </wd-column>
        </el-table>
      </slot>
    </div>
    <!-- 表格底部 -->
    <div
      class="_wd-table-footer flex justify-end mt-4"
      ref="crudFooterRef"
      v-if="total > 0 && openPagination && !tableOptions.tablePagination">
      <el-pagination
        v-model:current-page="requestParams[config.request.page]"
        v-model:page-size="requestParams[config.request.pageSize]"
        :page-sizes="[10, 20, 50, 100]"
        :background="true"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
        @size-change="pageSizeChangeHandler"
        @current-change="pageChangeHandler">
      </el-pagination>
    </div>
    <!-- Table表单 start -->
    <wd-crud-form ref="crudFormRef" @success="requestSuccess">
      <template v-for="slot in Object.keys($slots)" #[slot]="component">
        <slot :name="slot" v-bind="component" />
      </template>
    </wd-crud-form>
    <!-- Table表单 end -->
  </div>

  <wd-setting
    ref="crudSettingRef"
    @onChangeSearchHide="initSearchColumns()"
    @onChangeColumnHide="changeColumn"></wd-setting>
</template>

<style lang="less">
._wd-table-footer {
  z-index: 10;
  width: 100%;
}

.__crud_bordered {
  border: 1px solid #f1f1f1;
}

.recovery-data {
  color: #fd4e4e;

  &.el-button:hover {
    background-color: var(--el-button-hover-bg-color);
    border-color: var(--el-button-hover-border-color);
    color: var(--el-button-hover-text-color);
    outline: none;
  }
}
</style>
